use super::*;

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(transparent)]
pub struct ValueType(sys::napi_valuetype);
impl ValueType {
	/// Represents the undefined JavaScript value
	pub const UNDEFINED: Self = Self(sys::napi_undefined);
	/// Represents the null JavaScript value
	pub const NULL: Self = Self(sys::napi_null);
	/// Represents a boolean JavaScript value
	pub const BOOLEAN: Self = Self(sys::napi_boolean);
	/// Represents a number JavaScript value
	pub const NUMBER: Self = Self(sys::napi_number);
	/// Represents a string JavaScript value
	pub const STRING: Self = Self(sys::napi_string);
	/// Represents a symbol JavaScript value
	pub const SYMBOL: Self = Self(sys::napi_symbol);
	/// Represents an object JavaScript value
	pub const OBJECT: Self = Self(sys::napi_object);
	/// Represents a function JavaScript value
	pub const FUNCTION: Self = Self(sys::napi_function);
	/// Represents an external JavaScript value
	pub const EXTERNAL: Self = Self(sys::napi_external);
	/// Represents a bigint JavaScript value
	pub const BIGINT: Self = Self(sys::napi_bigint);
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum CoerceType {
	/// Coerce to boolean type
	Bool,
	/// Coerce to number type
	Number,
	/// Coerce to object type
	Object,
	/// Coerce to string type
	String,
}

#[derive(Clone, Copy)]
pub struct Value<'val> {
	pub(crate) env: &'val Env,
	pub(crate) inner: sys::napi_value,
}

impl<'val> Value<'val> {
	// Constructor and basic methods
	/// Creates a new Value instance
	pub fn new<'env>(env: &'env Env, value: sys::napi_value) -> Value<'env> {
		Value { env, inner: value }
	}

	pub fn _new<'v>(v: Value<'v>) -> Value<'v> {
		v
	}

	/// Returns the underlying napi_value
	pub fn raw(&self) -> sys::napi_value {
		self.inner
	}

	/// Creates a Value from a type that implements IntoNapi
	pub fn of<'env, T: IntoNapi>(env: &'env Env, value: T) -> Result<'static, Value<'env>> {
		value.into_napi(env)
	}

	/// Converts a Value to a type that implements FromNapi
	pub fn to<'env, T: FromNapi<'val>>(self) -> Result<'static, T> {
		T::from_napi(self)
	}

	// Property operations
	/// Defines properties on the value
	pub fn define(&'_ self, properties: &[PropertyDescriptor]) -> Result<'static, ()> {
		unsafe { sys::napi_define_properties(self.env.raw(), self.inner, properties.len(), properties.as_ptr() as _) }
			.result("napi_define_properties")
	}

	/// Checks if the value has an own property
	pub fn has_own_property(&'_ self, key: Value<'_>) -> Result<'static, bool> {
		let mut ret = false;
		unsafe { sys::napi_has_own_property(self.env.raw(), self.inner, key.raw(), &mut ret) }.result("napi_has_own_property")?;
		Ok(ret)
	}

	/// Sets a property on the value
	pub fn set_property(&'_ self, key: Value<'_>, value: Value<'_>) -> Result<'static, ()> {
		unsafe { sys::napi_set_property(self.env.raw(), self.inner, key.raw(), value.raw()) }.result("napi_set_property")
	}

	/// Checks if the value has a property
	pub fn has_property(&'_ self, key: Value<'_>) -> Result<'static, bool> {
		let mut ret = false;
		unsafe { sys::napi_has_property(self.env.raw(), self.inner, key.raw(), &mut ret) }.result("napi_has_property")?;
		Ok(ret)
	}

	/// Gets a property from the value
	pub fn get_property(&'_ self, key: Value<'_>) -> Result<'static, Value<'val>> {
		let mut ret = core::ptr::null_mut();
		unsafe { sys::napi_get_property(self.env.raw(), self.inner, key.raw(), &mut ret) }.result("napi_get_property")?;
		Ok(Value::new(self.env, ret))
	}

	/// Gets a property or returns a default value
	pub fn get_property_or_default(&'_ self, key: Value<'_>, default: Value<'val>) -> Result<'static, Value<'val>> {
		if self.has_property(key)? {
			self.get_property(key)
		} else {
			Ok(default)
		}
	}

	/// Gets a named property or returns a default value
	pub fn get_named_or_default(&'_ self, key: &core::ffi::CStr, default: Value<'val>) -> Result<'static, Value<'val>> {
		if self.has_named(key)? {
			self.get_named(key)
		} else {
			Ok(default)
		}
	}

	/// Deletes a property from the value
	pub fn delete_property(&'_ self, key: Value<'_>) -> Result<'static, bool> {
		let mut ret = false;
		unsafe { sys::napi_delete_property(self.env.raw(), self.inner, key.raw(), &mut ret) }.result("napi_delete_property")?;
		Ok(ret)
	}

	/// Sets a named property
	pub fn set_named(&'_ self, key: &core::ffi::CStr, value: Value<'_>) -> Result<'static, ()> {
		unsafe { sys::napi_set_named_property(self.env.raw(), self.inner, key.as_ptr(), value.raw()) }.result("napi_set_element")
	}

	/// Checks if a named property exists
	pub fn has_named(&'_ self, key: &core::ffi::CStr) -> Result<'static, bool> {
		let mut ret = false;
		unsafe { sys::napi_has_named_property(self.env.raw(), self.inner, key.as_ptr(), &mut ret) }.result("napi_has_element")?;
		Ok(ret)
	}

	/// Gets a named property
	pub fn get_named(&'_ self, key: &core::ffi::CStr) -> Result<'static, Value<'val>> {
		let mut ret = core::ptr::null_mut();
		unsafe { sys::napi_get_named_property(self.env.raw(), self.inner, key.as_ptr(), &mut ret) }.result("napi_get_element")?;
		Ok(Value::new(self.env, ret))
	}

	/// Gets the prototype of the value
	pub fn prototype(&'_ self) -> Result<'static, Value<'val>> {
		let mut ret = core::ptr::null_mut();
		unsafe { sys::napi_get_prototype(self.env.raw(), self.inner, &mut ret) }.result("napi_get_prototype")?;
		Ok(Value::new(self.env, ret))
	}

	// Method operations
	/// Gets a method as a Function
	pub fn get_method(&'_ self, method: &core::ffi::CStr) -> Result<'static, Function<'_>> {
		let method = self.get_named(method)?.to::<Function>()?;
		Ok(method)
	}
	/// Gets a method as a reference
	pub fn get_method_reference(&'_ self, method: &core::ffi::CStr) -> Result<'static, Reference> {
		let method = self.get_named(method)?.to::<Function>()?;
		Reference::with_initial_refcount(method.v(), 1)
	}
	/// Calls a method on the value with the given arguments
	pub fn call_method<Ret: FromNapi<'val>, T: ArgsBuilder>(&'_ self, method: &Function<'val>, args: T) -> Result<'static, Ret> {
		method.call::<Self, Ret, T>(Some(*self), args)
	}
	/// Calls a method on the value with the given arguments
	pub fn call_method_reference<Ret: FromNapi<'val>, T: ArgsBuilder>(&'_ self, method: &Reference, args: T) -> Result<'static, Ret> {
		let method = method.clone().into_napi(self.env)?.to::<Function>()?;
		method.call::<Self, Ret, T>(Some(*self), args)
	}
	/// Calls a method on the value with the given arguments
	pub fn call_named<Ret: FromNapi<'val>, T: ArgsBuilder>(&'_ self, method: &core::ffi::CStr, args: T) -> Result<'static, Ret> {
		let method = self.get_named(method)?.to::<Function>()?;
		method.call::<Self, Ret, T>(Some(*self), args)
	}
	pub fn call_static<Ret: FromNapi<'val>, T: ArgsBuilder>(&'_ self, method: &core::ffi::CStr, args: T) -> Result<'static, Ret> {
		let method = self.get_named(method)?.to::<Function>()?;
		method.call::<Self, Ret, T>(None, args)
	}

	// Type checking methods
	/// Gets the type of the value
	pub fn type_of(&'_ self) -> Result<'static, ValueType> {
		let mut ret = sys::napi_undefined;
		unsafe { sys::napi_typeof(self.env.raw(), self.inner, &mut ret) }.result("napi_typeof")?;
		Ok(ValueType(ret))
	}

	/// Checks if the value is an instance of the constructor
	pub fn instanceof(&'_ self, constructor: &'_ Value<'_>) -> Result<'static, bool> {
		let mut ret = false;
		unsafe { sys::napi_instanceof(self.env.raw(), self.inner, constructor.raw(), &mut ret) }.result("napi_instanceof")?;
		Ok(ret)
	}

	// Check if the value is an instance of the class
	pub fn is_instance<T: NapiClass>(&'_ self) -> Result<'static, bool> {
		let constructor = T::class();
		let constructor = constructor.ok_or_else(|| Status::invalid_arg("class not defined"))?;
		let constructor = constructor.into_napi(self.env)?;
		self.instanceof(&constructor)
	}

	/// Checks if the value is an error
	pub fn is_error(&'_ self) -> Result<'static, bool> {
		let mut ret = false;
		unsafe { sys::napi_is_error(self.env.raw(), self.inner, &mut ret) }.result("napi_is_error")?;
		Ok(ret)
	}

	/// Checks if the value is a date
	pub fn is_date(&'_ self) -> Result<'static, bool> {
		let mut ret = false;
		unsafe { sys::napi_is_date(self.env.raw(), self.inner, &mut ret) }.result("napi_is_date")?;
		Ok(ret)
	}

	/// Checks if the value is an array
	pub fn is_array(&'_ self) -> Result<'static, bool> {
		let mut ret = false;
		unsafe { sys::napi_is_array(self.env.raw(), self.inner, &mut ret) }.result("napi_is_array")?;
		Ok(ret)
	}

	/// Checks if the value is a buffer
	pub fn is_buffer(&'_ self) -> Result<'static, bool> {
		let mut ret = false;
		unsafe { sys::napi_is_buffer(self.env.raw(), self.inner, &mut ret) }.result("napi_is_buffer")?;
		Ok(ret)
	}

	/// Checks if the value is an arraybuffer
	pub fn is_arraybuffer(&'_ self) -> Result<'static, bool> {
		let mut ret = false;
		unsafe { sys::napi_is_arraybuffer(self.env.raw(), self.inner, &mut ret) }.result("napi_is_arraybuffer")?;
		Ok(ret)
	}

	/// Checks if the value is a typedarray
	pub fn is_typedarray(&'_ self) -> Result<'static, bool> {
		let mut ret = false;
		unsafe { sys::napi_is_typedarray(self.env.raw(), self.inner, &mut ret) }.result("napi_is_typedarray")?;
		Ok(ret)
	}

	/// Checks if the value is a dataview
	pub fn is_dataview(&'_ self) -> Result<'static, bool> {
		let mut ret = false;
		unsafe { sys::napi_is_dataview(self.env.raw(), self.inner, &mut ret) }.result("napi_is_dataview")?;
		Ok(ret)
	}

	/// Checks if the value is a promise
	pub fn is_promise(&'_ self) -> Result<'static, bool> {
		let mut ret = false;
		unsafe { sys::napi_is_promise(self.env.raw(), self.inner, &mut ret) }.result("napi_is_promise")?;
		Ok(ret)
	}

	/// Checks if the value is undefined
	pub fn is_undefined(&'_ self) -> Result<'static, bool> {
		Ok(self.type_of()? == ValueType::UNDEFINED)
	}

	/// Checks if the value is null
	pub fn is_null(&'_ self) -> Result<'static, bool> {
		Ok(self.type_of()? == ValueType::NULL)
	}

	/// Checks if the value is a boolean
	pub fn is_boolean(&'_ self) -> Result<'static, bool> {
		Ok(self.type_of()? == ValueType::BOOLEAN)
	}

	/// Checks if the value is a number
	pub fn is_number(&'_ self) -> Result<'static, bool> {
		Ok(self.type_of()? == ValueType::NUMBER)
	}

	/// Checks if the value is a string
	pub fn is_string(&'_ self) -> Result<'static, bool> {
		Ok(self.type_of()? == ValueType::STRING)
	}

	/// Checks if the value is a symbol
	pub fn is_symbol(&'_ self) -> Result<'static, bool> {
		Ok(self.type_of()? == ValueType::SYMBOL)
	}

	/// Checks if the value is an object
	pub fn is_object(&'_ self) -> Result<'static, bool> {
		Ok(self.type_of()? == ValueType::OBJECT)
	}

	/// Checks if the value is a function
	pub fn is_function(&'_ self) -> Result<'static, bool> {
		Ok(self.type_of()? == ValueType::FUNCTION)
	}

	/// Checks if the value is external
	pub fn is_external(&'_ self) -> Result<'static, bool> {
		Ok(self.type_of()? == ValueType::EXTERNAL)
	}

	/// Checks if the value is a bigint
	pub fn is_bigint(&'_ self) -> Result<'static, bool> {
		Ok(self.type_of()? == ValueType::BIGINT)
	}

	// Coercion methods
	/// Coerces the value to the specified type
	pub fn coerce(&'_ self, t: CoerceType) -> Result<'static, Value<'val>> {
		let mut ret = core::ptr::null_mut();
		match t {
			CoerceType::Bool => unsafe { sys::napi_coerce_to_bool(self.env.raw(), self.inner, &mut ret) } //
				.result("napi_coerce_to_bool"),
			CoerceType::Number => unsafe { sys::napi_coerce_to_number(self.env.raw(), self.inner, &mut ret) } //
				.result("napi_coerce_to_number"),

			CoerceType::Object => unsafe { sys::napi_coerce_to_object(self.env.raw(), self.inner, &mut ret) } //
				.result("napi_coerce_to_object"),
			CoerceType::String => unsafe { sys::napi_coerce_to_string(self.env.raw(), self.inner, &mut ret) } //
				.result("napi_coerce_to_string"),
		}?;
		Ok(Value::new(self.env, ret))
	}

	/// Coerces the value to a boolean
	pub fn coerce_bool(&'_ self) -> Result<'static, Value<'val>> {
		self.coerce(CoerceType::Bool)
	}

	/// Coerces the value to an object
	pub fn coerce_object(&'_ self) -> Result<'static, Value<'val>> {
		self.coerce(CoerceType::Object) // 修正：应该是CoerceType::Object而不是CoerceType::Bool
	}

	/// Coerces the value to a number
	pub fn coerce_number(&'_ self) -> Result<'static, Value<'val>> {
		self.coerce(CoerceType::Number)
	}

	/// Coerces the value to a string
	pub fn coerce_string(&'_ self) -> Result<'static, Value<'val>> {
		self.coerce(CoerceType::String)
	}

	// Value comparison and conversion
	/// Checks if two values are strictly equal
	pub fn strict_equals<'env>(&'_ self, other: &Value<'env>) -> Result<'static, bool> {
		let mut ret = false;
		unsafe { sys::napi_strict_equals(self.env.raw(), self.inner, other.inner, &mut ret) }.result("napi_strict_equals")?;
		Ok(ret)
	}

	/// Converts the value to a JSON string
	pub fn to_json(&'_ self) -> Result<'static, String> {
		let json_string = self.coerce_string()?.to()?;
		Ok(json_string)
	}

	/// Creates a value from a JSON string
	pub fn from_json<'env>(env: &'env Env, json: &str) -> Result<'static, Value<'env>> {
		let json_value = json.into_napi(env)?;
		let mut ret = core::ptr::null_mut();
		unsafe { sys::napi_run_script(env.raw(), json_value.inner, &mut ret) }.result("napi_run_script")?;
		Ok(Value::new(env, ret))
	}

	/// Creates a deep clone of the value
	pub fn deep_clone(&'_ self) -> Result<'static, Value<'val>> {
		// Using JSON serialization/deserialization to implement deep cloning
		let json = self.to_json()?;
		Self::from_json(self.env, &json)
	}

	// Truthiness checking
	/// Checks if the value is truthy
	pub fn is_truthy(&'_ self) -> Result<'static, bool> {
		if self.is_undefined()? || self.is_null()? {
			Ok(false)
		} else if self.is_boolean()? {
			(*self).to()
		} else if self.is_number()? {
			let num: f64 = (*self).to()?;
			Ok(num != 0.0 && !num.is_nan())
		} else if self.is_string()? {
			Ok(!(*self).to::<String>()?.is_empty())
		} else {
			// Objects and functions are typically truthy
			Ok(true)
		}
	}

	/// Checks if the value is falsy
	pub fn is_falsy(&'_ self) -> Result<'static, bool> {
		Ok(!self.is_truthy()?)
	}

	/// Gets a reference to wrapped data
	pub unsafe fn ref_wrap<T: 'static>(&'_ self) -> Result<'static, &'val T> {
		let mut ret = core::ptr::null_mut();
		sys::napi_unwrap(self.env.raw(), self.inner, &mut ret).result("napi_unwrap")?;
		mem::ref_raw_data(ret).ok_or(Status::invalid_arg("downcast_ref fail in ref_wrap"))
	}

	/// Gets a mutable reference to wrapped data
	pub unsafe fn mut_wrap<T: 'static>(&'_ self) -> Result<'static, &'val mut T> {
		let mut ret = core::ptr::null_mut();
		sys::napi_unwrap(self.env.raw(), self.inner, &mut ret).result("napi_unwrap")?;
		mem::mut_raw_data(ret).ok_or(Status::invalid_arg("downcast_mut fail in mut_wrap"))
	}

	/// Gets a reference to external data
	pub unsafe fn ref_external<T: 'static>(&'_ self) -> Result<'static, &'val T> {
		let mut ret = core::ptr::null_mut();
		sys::napi_get_value_external(self.env.raw(), self.inner, &mut ret).result("napi_get_value_external")?;
		mem::ref_raw_data(ret).ok_or(Status::invalid_arg("downcast_ref fail in ref_external"))
	}

	/// Gets a mutable reference to external data
	pub unsafe fn mut_external<T: 'static>(&'_ self) -> Result<'static, &'val mut T> {
		let mut ret = core::ptr::null_mut();
		sys::napi_get_value_external(self.env.raw(), self.inner, &mut ret).result("napi_get_value_external")?;
		mem::mut_raw_data(ret).ok_or(Status::invalid_arg("downcast_mut fail in mut_external"))
	}

	/// Wraps data with the value
	pub fn wrap<T: NapiDrop + 'static>(&'_ self, v: T) -> Result<'static, ()> {
		let data = mem::into_raw_data(v);
		unsafe {
			sys::napi_wrap(
				self.env.raw(),
				self.inner,
				data,
				Some(mem::finalizer::<T>),
				core::ptr::null_mut(),
				core::ptr::null_mut(),
			)
		}
		.result("napi_wrap")
	}

	/// Takes wrapped data from the value
	pub unsafe fn take_wrap(&'_ self) -> Result<'static, Box<dyn core::any::Any>> {
		let mut ret = core::ptr::null_mut();
		sys::napi_remove_wrap(self.env.raw(), self.inner, &mut ret).result("napi_remove_wrap")?;
		Ok(mem::from_raw_data(ret))
	}

	/// Adds a finalizer to the value
	pub fn napi_add_finalizer(
		&'_ self,
		finalizer: sys::napi_finalize,
		obj: *mut core::ffi::c_void,
		hint: *mut core::ffi::c_void,
	) -> Result<'static, ()> {
		unsafe { sys::napi_add_finalizer(self.env.raw(), self.inner, obj, finalizer, hint, core::ptr::null_mut()) }
			.result("napi_add_finalizer")
	}
}

impl<'val> FromNapi<'val> for Value<'val> {
	/// Converts a Value to a Value (identity conversion)
	fn from_napi(v: Value<'val>) -> Result<'static, Self> {
		Ok(v)
	}
}

impl<'val> IntoNapi for Value<'val> {
	/// Converts a Value to a Value in a different environment
	fn into_napi<'env>(self, env: &'env Env) -> Result<'static, Value<'env>> {
		Ok(Value { env, inner: self.inner })
	}
}

/// Represents a property descriptor for defining JavaScript properties
#[repr(transparent)]
pub struct PropertyDescriptor<'val> {
	pub inner: sys::napi_property_descriptor,
	_mark: core::marker::PhantomData<&'val ()>,
}

impl<'val> Default for PropertyDescriptor<'val> {
	/// Creates a default PropertyDescriptor
	fn default() -> Self {
		Self {
			inner: sys::napi_property_descriptor {
				utf8name: core::ptr::null_mut(),
				name: core::ptr::null_mut(),
				method: None,
				getter: None,
				setter: None,
				value: core::ptr::null_mut(),
				attributes: sys::napi_default,
				data: core::ptr::null_mut(),
			},
			_mark: Default::default(),
		}
	}
}

impl<'val> PropertyDescriptor<'val> {
	/// Sets the name of the property as a C string
	pub fn name_cstr(mut self, name: &'val std::ffi::CStr) -> Self {
		self.inner.utf8name = name.as_ptr();
		self
	}

	/// Sets the name of the property as a JavaScript value
	pub fn name_jsv(mut self, name: sys::napi_value) -> Self {
		self.inner.name = name;
		self
	}

	/// Sets the method of the property
	pub fn method(mut self, method: sys::napi_callback) -> Self {
		self.inner.method = method;
		self
	}

	/// Sets the getter of the property
	pub fn getter(mut self, getter: sys::napi_callback) -> Self {
		self.inner.getter = getter;
		self
	}

	/// Sets the setter of the property
	pub fn setter(mut self, setter: sys::napi_callback) -> Self {
		self.inner.setter = setter;
		self
	}

	/// Sets both getter and setter of the property
	pub fn getset(mut self, getter: sys::napi_callback, setter: sys::napi_callback) -> Self {
		self.inner.getter = getter;
		self.inner.setter = setter;
		self
	}

	/// Sets the value of the property
	pub fn value(mut self, value: sys::napi_value) -> Self {
		self.inner.value = value;
		self
	}

	/// Sets the attributes of the property
	pub fn attrs(mut self, attributes: sys::napi_property_attributes) -> Self {
		self.inner.attributes = attributes;
		self
	}

	/// Sets the data associated with the property
	pub fn data(mut self, data: *mut std::ffi::c_void) -> Self {
		self.inner.data = data;
		self
	}
}
